#pragma once

#include <iostream>
#include <cerrno>
#include "Connection.hpp"
#include "Log.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"

const static int buffer_size = 1024;

class HandlerConnection
{
public:
    // 处理请求
    static void HandlerRequest(Connection *conn)
    {
        string &inbuffer = conn->GetInbuffer();
        string messages; // 从inbuffer中截取的一个完整报文
        Factory factory;

        auto req = factory.BuildRequest();

        Calculator cal; // 负责业务

        // 明确报文边界，解决粘包问题
        while (DeCode(inbuffer, &messages))
        {
            // 反序列化，得到完整报文再进行后序操作
            if (!req->Deserialize(messages))
                continue;

            // 业务处理
            auto resp = cal.Cal(req);

            // 序列化
            string info;
            resp->Serialize(&info);

            // 封装完整报文
            string outmessage = EnCode(info);

            // 将报文加入输出缓冲区
            conn->AddOutBuffer(outmessage);
        }

        if (!conn->OutBufferEmpty())
            conn->_writer(conn);
    }

    // 读
    static void Reader(Connection *conn)
    {
        errno = 0;

        string &inbuffer = conn->GetInbuffer();
        // 不能保证一次性读完，需要重复读
        while (true)
        {
            char buffer[1024];
            ssize_t n = recv(conn->GetSockfd(), buffer, sizeof(buffer) - 1, 0);

            if (n > 0)
            {
                buffer[n] = 0;
                conn->AddInBuffer(buffer);
            }
            else
            {
                if (errno == EAGAIN)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg.LogMessage(Info, "reader error , becasue : %s , errno : \n", strerror(errno), errno);
                    conn->_excepter(conn);
                    return;
                }
            }
            // 读取完毕后，可能出现完整报文，尝试处理报文
            HandlerRequest(conn);
        }
    }

    // 写
    static void Writer(Connection *conn)
    {
        errno = 0;

        string &outbuffer = conn->GetOutbuffer();

        // 不能保证一次性写完，需要重复写
        while (true)
        {
            ssize_t n = send(conn->GetSockfd(), outbuffer.c_str(), outbuffer.size(), 0);

            if (n >= 0)
            {
                // 发送完毕后，将数据从输出缓冲区中删除
                outbuffer.erase(0, n);

                if (conn->OutBufferEmpty())
                    break;
            }
            else
            {
                if (errno == EAGAIN)
                {
                    break;
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    lg.LogMessage(Info, "writer error , becasue : %s , errno : \n", strerror(errno), errno);
                    conn->_excepter(conn);
                    return;
                }
            }

            if (!conn->OutBufferEmpty())
                conn->_R->EnableReadWrite(conn, true, true);
            else
                conn->_R->EnableReadWrite(conn, true, false);
        }
    }

    // 异常处理
    static void Excepter(Connection *conn)
    {
        errno = 0;
        lg.LogMessage(Info, "connection erase, sockfd: %d\n", conn->GetSockfd());
        // 从unordered_map中删除该连接
        conn->_R->DelConnection(conn->GetSockfd());
        conn->Close(conn->GetSockfd());

        delete conn;
    }
};